package com.tl.boot.main.aspect;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.tl.boot.common.annotation.WithPermission;
import com.tl.boot.common.constants.CommonConstant;
import com.tl.boot.common.enums.ResultEnum;
import com.tl.boot.common.exception.BaseException;
import com.tl.boot.common.utils.LogUtil;
import com.tl.boot.common.utils.PermissionUtil;
import com.tl.boot.common.vo.ResponseVO;
import com.tl.boot.common.wrapper.DecorateHttpServletRequestWrapper;
import com.tl.boot.common.config.PublicApiConfig;
import com.tl.boot.entity.demo.form.Base.BaseForm;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * com.tl.boot.main.aspect -- RequestAspect
 * Description:
 * Created by joshua_liu <a href="mailto:joshualwork@163.com"></a>
 * On 2019/9/10 18:52
 */
@Order(-99)
@Aspect
@Component
public class RequestAspect {
    @Autowired
    private PublicApiConfig publicApiConfig;

    @Pointcut("execution(public * com.tl.boot.controller..*.*(..))")
    private void allRequestMethod() {
    }

    @Around("allRequestMethod()")
    public Object doAround(ProceedingJoinPoint call) {
        Long startTimeStamp = System.currentTimeMillis();
        MethodSignature methodSignature = (MethodSignature) call.getSignature();
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        DecorateHttpServletRequestWrapper requestWrapper = (DecorateHttpServletRequestWrapper) servletRequestAttributes.getRequest();
        JSONObject jsonObj = JSON.parseObject(requestWrapper.getParams());
        String uri = requestWrapper.getRequestURI();
        Long userId = null;
        if (jsonObj != null && jsonObj.get("userId") != null) {
            userId = Long.parseLong(jsonObj.get("userId").toString());
        }
        BaseForm requestArgs = null;
        Exception exception = null;
        Object result = "";

        if (call.getArgs().length != 0) {
            for (Object object : call.getArgs()) {
                if (object instanceof BaseForm) {
                    requestArgs = (BaseForm) object;
                    break;
                }
            }
        }
        try {
            if (requestArgs == null) {
                if (uriPermissionCheck(uri)) {
                    checkPermission(userId, requestWrapper.getRequestURI(), methodSignature.getMethod());
                }
            } else {
                requestArgs.checkParam();
                if (uriPermissionCheck(uri)) {
                    checkPermission(userId, requestWrapper.getRequestURI(), methodSignature.getMethod());
                }
            }
            result = call.proceed();
        } catch (BaseException e) {
            result = ResponseVO.buildFail(e.getErrorEnum());
            LogUtil.warn(requestWrapper, result.toString(), System.currentTimeMillis() - startTimeStamp);
        } catch (Exception e) {
            exception = e;
            result = ResponseVO.buildFail(ResultEnum.SYSTEM_UNKNOWN);
            LogUtil.error(requestWrapper, result.toString(), System.currentTimeMillis() - startTimeStamp, exception);
        } finally {
            if (exception == null) {
                LogUtil.info(requestWrapper, result + "", System.currentTimeMillis() - startTimeStamp);
            }
            return result;
        }
    }

    private boolean uriPermissionCheck(String uri) {
        AntPathMatcher matcher = new AntPathMatcher();
        String[] apiPatterns = publicApiConfig.getApiPatterns();
        if (apiPatterns != null && apiPatterns.length > 0) {
            for (int i = 0; i < apiPatterns.length; i++) {
                if (matcher.match(apiPatterns[i], uri)) {
                    return false;
                }
            }
        }
        return true;
    }

    private void checkPermission(Long userId, String api, Method method) {
        //用userid 去redis获取用户的角色信息和权限列表
        WithPermission withPermission = null;
        if (method.getDeclaringClass().getAnnotation(WithPermission.class) != null) {
            withPermission = method.getDeclaringClass().getAnnotation(WithPermission.class);
        } else {
            withPermission = method.getAnnotation(WithPermission.class);
        }
        JSONObject permissionInfo = null;
        List<String> currentUserHasRoles = null;
        List<String> currentUserHasPermission = null;
        if (PermissionUtil.getPermissionInfo(userId) != null) {
            permissionInfo = PermissionUtil.getPermissionInfo(userId);
            currentUserHasRoles = (List<String>) permissionInfo.get(CommonConstant.ROLE_LIST);
            currentUserHasPermission = (List<String>) permissionInfo.get(CommonConstant.ROSOURCE_LIST);
        }
        if (withPermission != null) {
            List<String> allowRoles = new ArrayList(Arrays.asList(withPermission.role()));
            LogUtil.debug(String.format("roles>>>%s  allow to operate", allowRoles.toString()));
            //求两个集合的交集，如果没有交集，说明该用户没有操作此接口的角色
            allowRoles.retainAll(currentUserHasRoles);
            if (allowRoles.size() == 0) {
                throw new BaseException(ResultEnum.SYSTEM_PRMISSION_DENIED);
            }
        } else {
            return;
        } /*else if (!currentUserHasPermission.contains(api)) {
            //如果权限列表没有该访问路径的话，说明该用户没有操作此接口的权限
            throw new BaseException(ResultEnum.SYSTEM_PRMISSION_DENIED);
        }*/
    }
}
